#include "stdafx.h"
#include "AfterBurnerClone.h"

#include "../Framework/Sound.h"
#include "../Framework/ContentManager.h"
#include "../Framework/Mouse.h"

namespace MyGame {

AfterBurnerClone::AfterBurnerClone(void) {
    this->cameraType = CAMERATYPE_FREE;
    this->keyTimeOut = 0;
    this->time = 0;
}

AfterBurnerClone::~AfterBurnerClone(void) {
}

EnemyController* AfterBurnerClone::GetEnemyController() const {
    return this->enemyController;
}

ParticleSystem* AfterBurnerClone::GetParticleSystem() const {
    return this->particleSystem;
}

void AfterBurnerClone::Draw(FLOAT timeDelta) {
    this->GetCamera()->BuildViewFrustum();
#ifdef _DEBUG
    this->terrain->SetVisible(false);
    this->treePlanter->SetVisible(false);
    this->particleSystem->SetVisible(false);
#else
    this->terrain->SetVisibilityRange(150);
    this->treePlanter->SetVisibilityRange(25);
#endif
    this->terrain->SetDrawUnderwater(false);
    this->terrain->UpdateVisibleParts();
    this->treePlanter->UpdateVisibleParts();
    this->lblTimeEllapsed->SetVisible(false);
    this->water->SetVisible(false);
    this->hud->SetVisible(false);
    this->DrawReflection(timeDelta);
#ifdef _DEBUG
    this->terrain->SetVisible(true);
    this->treePlanter->SetVisible(true);
    this->particleSystem->SetVisible(true);
#else
    this->terrain->SetVisibilityRange(300);
    this->treePlanter->SetVisibilityRange(50);
#endif
    this->terrain->SetDrawUnderwater(true);
    this->terrain->UpdateVisibleParts();
    this->treePlanter->UpdateVisibleParts();
    this->lblTimeEllapsed->SetVisible(true);
    this->water->SetVisible(true);
    this->hud->SetVisible(true);
    __super::Draw(timeDelta);
}

void AfterBurnerClone::DrawReflection(FLOAT timeDelta) {
    this->GetCamera()->SetMirror(true);
    IDirect3DSurface9* surface;
    this->GetGraphicsDevice()->GetRenderTarget(0, &surface);

    IDirect3DSurface9* reflectionSurface;
    this->reflectionTexture->GetSurfaceLevel(0, &reflectionSurface);
    this->GetGraphicsDevice()->SetRenderTarget(0, reflectionSurface);
    this->GetGraphicsDevice()->Clear(NULL, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, 0x00000000, 1.0f, NULL);
    this->GetGraphicsDevice()->SetRenderTarget(0, reflectionSurface);
    this->GetGraphicsDevice()->SetTransform(D3DTS_VIEW, &this->skyBoxCamera->GetViewMatrix());
    __super::Draw(timeDelta);
    this->GetGraphicsDevice()->SetRenderTarget(0, surface);
    this->GetGraphicsDevice()->SetTransform(D3DTS_VIEW, &this->GetCamera()->GetViewMatrix());
    this->GetCamera()->SetMirror(false);
    this->GetGraphicsDevice()->Clear(NULL, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0x00000000, 1.0f, NULL);
}

void AfterBurnerClone::Finalize() {
}

void AfterBurnerClone::Initialize() {
    this->GetGraphicsDevice()->CreateTexture(
        512,
        512,
        1,
        D3DUSAGE_RENDERTARGET,
        D3DFMT_X8R8G8B8,
        D3DPOOL_DEFAULT,
        &this->reflectionTexture,
        NULL);

    D3DXMATRIX matrixProjection;
    this->skyBoxCamera = new Camera(this->GetGraphicsDevice());
    this->skyBoxCamera->SetProjectionMatrix(this->GetCamera()->GetProjectionMatrix());
    this->skyBoxCamera->SetMirror(true);
    this->skyBoxCamera->SetVisibilityRange(20);

    this->skyBox = new SkyBox(this);
    skyBox->SetScale(D3DXVECTOR3(100, 100, 100));
    this->GetComponents()->Add(skyBox);

    this->terrain = new Terrain(this);
    terrain->SetDrawPartialTerrain(true);
    terrain->SetHeightMapScale(D3DXVECTOR3(1, 20.0f, 1));
    terrain->SetHeightMapDimensions(200, 2000);
    this->GetComponents()->Add(terrain);

    this->water = new Water(this, 2.0f, 200, 2000);
    this->water->SetReflectionTexture(this->reflectionTexture);
    this->GetComponents()->Add(this->water);

    this->treePlanter = new TreePlanter(this);
    this->treePlanter->SetTreeLineHeights(
        this->terrain->GetHeightWater() * this->terrain->GetHeightMapScale().y,
        this->terrain->GetHeightSand() * this->terrain->GetHeightMapScale().y,
        this->terrain->GetHeightGrass() * this->terrain->GetHeightMapScale().y,
        this->terrain->GetHeightRock() * this->terrain->GetHeightMapScale().y);
    this->treePlanter->SetTerrain(this->terrain);
    this->treePlanter->SetLodRange(0);
    this->GetComponents()->Add(treePlanter);

    this->player = new Player(this);
    this->player->SetEnabled(false);
    this->player->SetPosition(D3DXVECTOR3(0, 3, -995));
    this->GetComponents()->Add(this->player);

    this->enemyController = new EnemyController(this);
    this->enemyController->SetEnabled(false);
    this->enemyController->SetPlayer(player);
    this->enemyController->SetTerrain(this->terrain);
    this->GetComponents()->Add(this->enemyController);

    this->particleSystem = new ParticleSystem(this);
    this->GetComponents()->Add(this->particleSystem);

    this->hud = new Hud(this);
    this->hud->SetEnemyController(this->enemyController);
    this->GetComponents()->Add(hud);

    this->lblTimeEllapsed = new Label(this);
    this->lblTimeEllapsed->bounds.top = 0;
    this->lblTimeEllapsed->bounds.left = 0;
    this->lblTimeEllapsed->bounds.bottom = 20;
    this->lblTimeEllapsed->bounds.right = 500;
    this->GetComponents()->Add(this->lblTimeEllapsed);

    this->GetCamera()->SetPosition(D3DXVECTOR3(0, 3, -1000));
    this->GetCamera()->SetMoveSpeed(7);
    this->GetCamera()->SetYawPitchRollSpeed(1);
    this->GetCamera()->SetVisibilityRange(50);

    this->GetGraphicsDevice()->SetTransform(D3DTS_VIEW, &this->GetCamera()->GetViewMatrix());
    this->GetGraphicsDevice()->SetRenderState(D3DRS_LIGHTING, false);

    this->GetGraphicsDevice()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_ANISOTROPIC);
    this->GetGraphicsDevice()->SetSamplerState(0, D3DSAMP_MIPFILTER, D3DTEXF_LINEAR);
    this->GetGraphicsDevice()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);

    __super::Initialize();
}

void AfterBurnerClone::LoadContent() {
    __super::LoadContent();
}

void AfterBurnerClone::UnloadContent() {
    __super::UnloadContent();
}

void AfterBurnerClone::Update(FLOAT timeDelta) {
    this->time += timeDelta;
    this->keyTimeOut += timeDelta;

    if (GetAsyncKeyState('C') & 0x8000f && this->keyTimeOut > 0.2) {
        if (this->cameraType == CAMERATYPE_CHASE) {
            this->cameraType = CAMERATYPE_FREE;
            this->player->SetEnabled(false);
            this->enemyController->SetEnabled(false);
            this->particleSystem->SetEnabled(false);
        } else if (this->cameraType == CAMERATYPE_FREE) {
            this->cameraType = CAMERATYPE_CHASE;
            this->player->SetEnabled(true);
            this->enemyController->SetEnabled(true);
            this->particleSystem->SetEnabled(true);
        }

        this->keyTimeOut = 0;
    }

    char buf[50];

    // Display simulation time
    _snprintf_s(buf, 50, "Frame draw time: %4.3f seconds", timeDelta);
    this->lblTimeEllapsed->SetText(string(buf));

    this->MoveJet(timeDelta);

    __super::Update(timeDelta);

    // Move Camera
    this->MoveCamera(timeDelta);
    D3DXVECTOR3 camPos = this->GetCamera()->GetPosition();
    this->GetSoundDevice()->GetListener()->SetPosition(camPos.x, camPos.y, camPos.z, DS3D_DEFERRED);
    D3DXVECTOR3 camLook = this->GetCamera()->GetLookAt();
    D3DXVECTOR3 camUp = this->GetCamera()->GetUpVector();
    this->GetSoundDevice()->GetListener()->SetOrientation(
        camLook.x,
        camLook.y,
        camLook.z,
        camUp.x,
        camUp.y,
        camUp.z,
        DS3D_DEFERRED);
    this->GetSoundDevice()->GetListener()->CommitDeferredSettings();
}

void AfterBurnerClone::MoveCamera(FLOAT timeDelta) {
    D3DXVECTOR3 cameraPosition;
    D3DXVECTOR3 lookAt;
    D3DXVECTOR3 position;
    bool changed = false;

    switch (this->cameraType) {
    case CAMERATYPE_CHASE:
        cameraPosition = this->player->GetPosition();
        cameraPosition.z -= 3;
        this->GetCamera()->SetMoveSpeed(this->player->GetSpeed() * 1.3f);
        this->GetCamera()->MoveTo(cameraPosition);
        lookAt = this->player->GetPosition() - cameraPosition;
        lookAt.y = 0;
        D3DXVec3Normalize(&lookAt, &lookAt);
        this->GetCamera()->SetLookAt(lookAt);
        changed = true;
        this->GetCamera()->Update(timeDelta);
        position = this->GetCamera()->GetPosition();
        if (position.x < cameraBoundary.min.x) {
            position.x = cameraBoundary.min.x;
        } else if (position.x > cameraBoundary.max.x) {
            position.x = cameraBoundary.max.x;
        }

        if (position.y < cameraBoundary.min.y) {
            position.y = cameraBoundary.min.y;
        } else if (position.y > cameraBoundary.max.y) {
            position.y = cameraBoundary.max.y;
        }

        position.z = this->player->GetPosition().z - 2;
        this->GetCamera()->SetPosition(position);
        break;
    case CAMERATYPE_FREE:
    default:
        if (GetAsyncKeyState('W') & 0x8000f) {
            this->GetCamera()->Walk(4.0f * timeDelta);
            changed = true;
        }

        if (GetAsyncKeyState('S') & 0x8000f) {
            this->GetCamera()->Walk(-4.0f * timeDelta);
            changed = true;
        }

        if (GetAsyncKeyState('A') & 0x8000f) {
            this->GetCamera()->Strafe(-4.0f * timeDelta);
            changed = true;
        }

        if (GetAsyncKeyState('D') & 0x8000f) {
            this->GetCamera()->Strafe(4.0f * timeDelta);
            changed = true;
        }

        if (GetAsyncKeyState(VK_LEFT) & 0x8000f) {
            this->GetCamera()->Turn(-1.0f * timeDelta);
            changed = true;
        }

        if (GetAsyncKeyState(VK_RIGHT) & 0x8000f) {
            this->GetCamera()->Turn(1.0f * timeDelta);
            changed = true;
        }
        break;
    }

    if (changed) {
        this->GetGraphicsDevice()->SetTransform(D3DTS_VIEW, &this->GetCamera()->GetViewMatrix());
    }
}

void AfterBurnerClone::MoveJet(FLOAT timeDelta) {
    if (this->cameraType != CAMERATYPE_CHASE || this->player->GetState() != FLYINGSTATE_ALIVE) {
        return;
    }

    D3DXVECTOR3 position = this->player->GetPosition();

    FLOAT surfaceHeight = this->terrain->GetHeightFromSurface(D3DXVECTOR3(position.x, 0, position.z));
    if (surfaceHeight < this->water->GetHeight()) {
        surfaceHeight = this->water->GetHeight();
    }

    jetBoundary.min = D3DXVECTOR2(-20, surfaceHeight + 1);
    jetBoundary.max = D3DXVECTOR2(20, surfaceHeight + 11);
    cameraBoundary.min = D3DXVECTOR2(jetBoundary.min.x + 1, jetBoundary.min.y + 1);
    cameraBoundary.max = D3DXVECTOR2(jetBoundary.max.x - 1, jetBoundary.max.y - 1);

    if (GetAsyncKeyState('A') & 0x8000f) {
        this->player->Accelerate();
    }

    if (GetAsyncKeyState('Z') & 0x8000f) {
        this->player->Decelerate();
    }

    const FLOAT speed = this->player->GetSpeed();

    if (GetAsyncKeyState(VK_LCONTROL) & 0x8000f || Mouse::GetInstance()->GetRightButton() == BUTTONSTATE_PRESSED) {
        this->player->ShootMissile();
    }

    if (GetAsyncKeyState(' ') & 0x8000f || Mouse::GetInstance()->GetLeftButton() == BUTTONSTATE_PRESSED) {
        this->player->ShootCannon();
    }

    if (GetAsyncKeyState(VK_UP) & 0x8000f || Mouse::GetInstance()->GetY() < -1) {
        position.y -= speed * timeDelta;
    }

    if (GetAsyncKeyState(VK_DOWN) & 0x8000f || Mouse::GetInstance()->GetY() > 1) {
        position.y += speed * timeDelta;
    }

    if (GetAsyncKeyState(VK_LEFT) & 0x8000f || Mouse::GetInstance()->GetX() < -1) {
        position.x -= speed * timeDelta;
    }

    if (GetAsyncKeyState(VK_RIGHT) & 0x8000f || Mouse::GetInstance()->GetX() > 1) {
        position.x += speed * timeDelta;
    }

    if (position.x < jetBoundary.min.x) {
        position.x = jetBoundary.min.x;
    }

    if (position.x > jetBoundary.max.x) {
        position.x = jetBoundary.max.x;
    }

    if (position.y < jetBoundary.min.y) {
        position.y = jetBoundary.min.y;
    }

    if (position.y > jetBoundary.max.y) {
        position.y = jetBoundary.max.y;
    }

    D3DXVECTOR3 jetRotation = this->player->GetRotation();
    D3DXVECTOR3 camRotation = D3DXVECTOR3(0, 0, 0);
    if (position.x > this->player->GetPosition().x) {
        jetRotation.z = -D3DX_PI / 3;
        camRotation.z = -D3DX_PI / 3;
    } else if (position.x < this->player->GetPosition().x) {
        jetRotation.z = D3DX_PI / 3;
        camRotation.z = D3DX_PI / 3;
    } else {
        jetRotation.z = 0;
        camRotation.z = 0;
    }

    if (position.y > this->player->GetPosition().y) {
        jetRotation.x = -D3DX_PI / 12;
    } else if (position.y < this->player->GetPosition().y) {
        jetRotation.x = D3DX_PI / 12;
    } else {
        jetRotation.x = 0;
    }

    this->player->YawPitchRollTo(jetRotation);
    this->GetCamera()->YawPitchRollTo(camRotation);
    this->player->SetPosition(position);
}

}